====== SSOCAS Plugin ====== ---- plugin ---- description: Provides Yale CAS single sign-on using the plain auth backend for users author : Iain Hallam email : iain@nineworlds.net type : action lastupdate : 2012-05-04 compatible : Anteater, Rincewind, Angua depends : conflicts : similar : openid, authplaincas tags : authentication,cas downloadurl: https://bitbucket.org/iainhallam/dokuwiki-plugin-ssocas/get/stable.zip bugtracker : https://bitbucket.org/iainhallam/dokuwiki-plugin-ssocas/issues?status=new&status=open sourcerepo : https://bitbucket.org/iainhallam/dokuwiki-plugin-ssocas ---- After looking at the [[auth:cas|existing CAS implementations]] for DokuWiki, I decided I would prefer a simpler alternative, where users are still defined in ''users.auth.php'' and only the actual authentication is supplied by the CAS server. This has the advantage of being able to maintain the local user file via (S)FTP but also means that users might occasionally try to change their password on your site, not at the CAS server. Caveat: I am not a security professional and this code is provided as-is (under GPLv2) for your experimentation. It has been in use on a membership site I put together for several years without problems, but suggestions for improvements are always welcome on this page. Thanks to Aurélien Veillas for spotting the need for ''act_permcheck''. ===== Installation ===== :!: This version is currently not compatible with modern versions of CAS.php - see the [[#development]] section for workarounds. I'm working on integrating the fixes. Search and install the plugin using the [[plugin:extension|Extension Manager]]. Refer to [[:Plugins]] on how to install plugins manually. The local plugin directory must have the same name as the plugin is named, otherwise the plugin won't work properly. ===== Usage ===== Once the plugin is installed, check your site's configuration settings, and you should find a new section for SSOCAS: ^Setting^Default^Help text^Comments^ |name|CAS Login|CAS login service name|What you want your users to see when the plugin refers to the CAS service| |localname|Local Login|Local login service name|This will be displayed on the normal login form to differentiate it from the CAS login.| |server| |CAS server DNS name|Just the server name, e.g. ''sso.example.com''.| |port|443|CAS server port|Only needed if your CAS service is not on the normal HTTPS port.| |uri| |CAS server path|Only needed if the CAS pages are not in ''/'' on the CAS server, e.g. ''sso''.| |version|2|CAS version (1 or 2 supported)| | |caslogout|No|Logout from CAS as well as DokuWiki?|:!: Currently not working, but a workaround is known (below) and will be incorporated.| |stickysession|No|Keep logins valid after the browser session ends?|This allows the session to go on for a year.| |logourl| |URL to a logo for the CAS service. If serving login pages via HTTPS, make sure this is either relative (/...) or an HTTPS URL.|E.g. ''/_media/cas-logo.png''; URLs without protocol also work: ''/''''/server/path''.| |jshidelocal|Yes|Use JavaScript to hide the local login form until required?| | ===== Development ===== ==== Changes ==== {{rss>https://bitbucket.org/iainhallam/dokuwiki-plugin-ssocas/rss 3}} ==== Known Issues ==== * None of the strings apart from settings help are localised. * CAS logout is not called in recent versions of DokuWiki (see below). * White screen of error VS. Redirect loop of doom (with recent versions of CAS.php; see below). === CAS Logout for recent dokuwiki version === In action.php file, there are 2 lines checking if $_SESSION[DOKU_COOKIE]['auth']['pass'] == 'CAS', so that plugin can call CAS logout url (if $conf['caslogout'] is set to 1 in default.php). It seems that recent version of dokuwiki use more password hash formats, so that this condition is always false, and CAS logout is never called. Replacing 'CAS' by its hashed version 'ab38a28edf84c693b1e1a48eb39299086b5ebe50' should make logout work again. === White screen of error VS. Redirect loop of doom === > Since release 2014-05-05 "Ponder Stibbons" you can modify session parameters in DokuWiki . See [[devel:auth_plugins#Start session customization]]. Could that solve some of these issues? > --- [[user>Klap-in|Klap-in]] //2014/03/24 15:05// As some people may have noticed, this no longer works with the latest version of phpCAS and DokuWiki. I am not the most familiar with the inner-workings of DokuWiki, or phpCAS. The error is that phpCAS has introduced an error message if it is editing an already existing session. However, if you tell phpCAS not to do any session, it can result in a redirect loop. As a quick fix, I removed the check to see if there is already an existing session in phpCAS (some lines in the 590s of client.php in phpCAS). If anybody comes up with a better fix, please let us know. --- [[user>cletnick]] //2010/10/11 05:33// == Half-working fix == This hack of inc/init.php makes the error go away and a login to be successful. Basically phpCAS::client is called before dokuwiki calls session_start, instead of being called in action.php when the session is already created. inc/init.php 129 include_once('CAS.php'); 130 phpCAS::client(CAS_VERSION_2_0,'cas.example.com',443,''); 131 session_start(); Another idea is do define('NOSESSION', true); in conf/local.php. This makes dokuwiki skip session handling which then succeeds for phpCAS but it also has the same problems as above. **Update**: A modification to handle_action to set authentication information at each page reload makes this work: if (phpCAS::checkAuthentication()) { global $ACT, $auth, $conf, $INFO, $USERINFO; // user logged in, fill auth info so dokuwiki is happy $casuser = phpCAS::getUser(); $_SERVER['REMOTE_USER'] = $casuser; $USERINFO = $auth->getUserData($casuser); $INFO = pageinfo(); } This is probably not the right way to do it, so please contribute if you know how. --- [[user>alfs]] //2011/02/11 11:27// == Proposal == I have been testing the plugin. In the function ''handle_action'' of file ''action.php'': * I had to add a last empty parameter (or set to false) when ''calling phpCAS::client(...)'', to avoid "phpCAS error: phpCAS::client(): Another session was started before phpcas." * Just after calling ''phpCAS::client'', I have added the instruction ''session_start();'', which seems to solve the infinite loop issue in my case. I don't know if it has bad impacts in the rest of the program... Here is what I did: [...] function handle_action (&$event, $param) { global $ACT; require_once ('CAS.php'); // AVEILLAS - 20110518 - CASsification dokuWiki: start of modif" //phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri')); phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri'), false); session_start(); // AVEILLAS - 20110518 - CASsification... end of modif." phpCAS::setNoCasServerValidation(); [...] Ca you tell me if it works for you too? --- Aurélien //2011/05/19// == Reply == Thank you Aurélien for your help, it seems that the ''session_start ()'' solves the multiredirection problems. In fact, when I put this line phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri')); or this line phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri'), false); there is an error like this phpCAS::client(): phpCAS::client() has already been called so I comment these two lines. Thank you again for your help ---24/05/2011 == Report == Now (after adding session_start(), adding last empty parameter in phpCAS::client call, changing 'CAS' password to hashed string, and check perms) everything seems to work well. --- Aurélien Veillas //2011/06/06// ==== Can Haz WhiteList? ==== After defeating the phpCAS updates of doom and successfully logging in, it was time to decide if I wanted to add all the users I wanted to be able to use CAS. Or, do I want to extend the plugin to allow account creation with a whitelist? Is that a challenge With my longing to refresh my simple PHP skills, I concluded that latter was a good plan. == Changes == What I did: * See note in Comments regarding my change to phpCAS. * Updated the logout function to not check if CAS is currently authenticated (This is a pain when debugging because CAS tries to save itself some calls to the CAS server and caches. Rapid setting changes in the debugging process result in this producing anomalous results. Also, no harm in sending the user to the CAS page even if they already logged out from CAS some other way.) * Updated the login function for increased flow control. Functionality added: * Account creation * Whitelist checking * Whitelist config option (Comma separated list of allowed usernames. If this is big, I suggest pasting into your favorite text editor to manipulate) * Email domain config option (When creating the user, the user's email will be set to username@this.setting. This is useful for many organizations that have email addresses based on usernames.) * WhiteList override. Do you want to allow all users to log in? Check if so. (Will disable whitelist functionality and just let everybody log in) * Enable account creation. Do you want accounts to be created by this plugin? Check if so. (If you have accounts in the whitelist and this is set to false, even those accounts won't be created) Note that I changed my specific install to user server validation. I suggest you do this as well. Also, this means I never was able to "test" the specific code I am posting (since I changed validation back in this code). If there is an error, it would likely be syntactical. Please feel free to make corrections if needed. == The Code == Here is the full action file with a modified login and logout function. 'Iain Hallam', 'email' => 'iain@iainhallam.com', 'date' => '2009-09-22', 'name' => 'SSO CAS Plugin', 'desc' => 'Authenticate DokuWiki users via CAS', 'url' => 'http://www.dokuwiki.org/plugin:ssocas', ); } function register (Doku_Event_Handler $controller) { if ($this->getConf('server') != '') { $controller->register_hook ('HTML_LOGINFORM_OUTPUT', 'BEFORE', $this, 'handle_login_form'); $controller->register_hook ('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'handle_action'); $controller->register_hook ('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'handle_template'); } } function _self () { global $ID; return wl($ID, '', true, ''); } function _selfdo ($do) { global $ID; return wl($ID, 'do=' . $do, true, '&'); } function _redirect ($url) { header ('Location: ' . $url); exit; } function handle_login_form (&$event, $param) { global $auth; global $conf; global $lang; global $ID; // Remove the register and resendpwd links, if they exist. for ($formPosition = 0; $formPosition < count($event->data->_content); $formPosition++) { $formElement = $event->data->getElementAt($formPosition); if ((! is_array($formElement)) and (substr($formElement, 0, 2) == 'data->replaceElement ($formPosition, NULL); } } $insertElement = 5; if($auth && $auth->canDo('addUser') && actionOK('register')){ $event->data->insertElement($insertElement,'

'.$lang['reghere'].': '.$lang['register'].'

'); $insertElement = 6; } if ($auth && $auth->canDo('modPass') && actionOK('resendpwd')) { $event->data->insertElement($insertElement,'

'.$lang['pwdforget'].': '.$lang['btn_resendpwd'].'

'); } if ($this->getConf('logourl') != '') { $caslogo = ' '; } else { $caslogo = ''; } $event->data->insertElement(0,'
'.$this->getConf('name').''); $event->data->insertElement(1,'

'.$caslogo.'Login

'); $event->data->insertElement(2,'
'); if ($this->getConf('jshidelocal')) { $event->data->insertElement(3,'

Only use this if you cannot use the '.$this->getConf('name').' above.

'); $event->data->replaceElement(4,'
'.$this->getConf('localname').''); } else { $event->data->replaceElement(3,'
'.$this->getConf('localname').''); } } function handle_caslogin () { global $ACT, $auth, $conf, $INFO, $USERINFO; //phpCAS::setFixedServiceURL(DOKU_URL . 'doku.php?id=' . $QUERY); phpCAS::forceAuthentication(); if (phpCAS::checkAuthentication()) { // Successful $casuser = phpCAS::getUser(); $USERINFO = $auth->getUserData($casuser); $wlstring = $this->getConf('wluserstring'); $wlstring = preg_replace('/(\s|\n|\r|\t)/', '', $wlstring); $wlusers = explode(",", $wlstring); if ((in_array($casuser, $wlusers)) || $this->getConf('allowall')) { if (empty($USERINFO)) { //No account yet if ($this->getConf('makenew')) { // Try making an account $tmpuserdomain = ($casuser."@".trim($this->getConf('useredomain'))); if (false == $auth->triggerUserMod('create', array($casuser, date(DATE_ATOM) ,$casuser, $tmpuserdomain))) { $ACT = 'denied'; msg ('CAS to Doku user creation error', -1); } //update userinfo with the new user $USERINFO = $auth->getUserData($cassuser); } else { $ACT = 'denied'; msg ('Your user has not been created, and CAS to Doku creation is disabled', -1); return; } } // Populate the session variables $_SERVER['REMOTE_USER'] = $casuser; if ($this->getConf('stickysession')) { $stickysession = true; } else { $stickysession = false; } auth_setCookie($casuser,'CAS',$stickysession); // Authentication info has changed: reset the page info $INFO = pageinfo(); $ACT = 'show'; } else { $ACT = 'denied'; msg ('Sorry; you are not on the allowed user list', -1); } } else { // Failed $ACT = 'denied'; msg ('Sorry; your login to '.$this->getConf('name').' failed.',-1); } } function handle_caslogout () { // Check CAS authentication and whether to log out of CAS completely, and do a phpCAS::logout if so. if ((isset($_SERVER['REMOTE_USER'])) && ($_SESSION[DOKU_COOKIE]['auth']['pass'] == 'CAS')) { if ($this->getConf('caslogout')) { phpCAS::logoutWithRedirectServiceAndUrl($this->_self(), $this->_self()); } } auth_logoff(); } function handle_action (&$event, $param) { global $ACT; require_once ('CAS.php'); phpCAS::client($this->getConf('version').'.0',$this->getConf('server'),(integer) $this->getConf('port'),$this->getConf('uri')); phpCAS::setNoCasServerValidation(); // Handle the case where the CAS session is finished but the user is still logged in to DokuWiki if (! $this->getConf('stickysession')) { if ((isset($_SERVER['REMOTE_USER'])) && ($_SESSION[DOKU_COOKIE]['auth']['pass'] == 'CAS')) { if (! phpCAS::checkAuthentication()) { // Authentication failed $event->preventDefault(); $this->handle_caslogout(); $this->_redirect($this->_self()); } } } if ($event->data == 'caslogin') { $event->preventDefault(); $this->handle_caslogin(); } if ($event->data == 'logout') { $this->handle_caslogout(); } } function handle_template (&$event, $param) { if ($event->data == 'caslogin') { $event->preventDefault(); } } } Here is the new defualt.php Here is the new metadata.php '/[0-9]*/'); $meta['uri'] = array('string'); $meta['version'] = array('string'); $meta['caslogout'] = array('onoff'); $meta['stickysession'] = array('onoff'); $meta['logourl'] = array('string'); $meta['jshidelocal'] = array('onoff'); $meta['wluserstring'] = array('string'); $meta['useredomain'] = array('string'); $meta['allowall'] = array('onoff'); $meta['makenew'] = array('onoff');; I agree with the comment in known issues that this could use better localised string support. However, I think it is reasonable for the person who wishes to port this to another language to implement that. And when tehy do, it would be great if it would be shared. The most common way comunity efforts move forward is that people create what they need, and then share it. Let's keep up the good work. --- [[user>cletnick]] //2010/10/11 05:32// ==== Set CAS login as default mecanism for frontend ==== Hi, i've just configured the plugin. \\ My default setup allow @all group no read access. \\ How it will be possible to configure the CAS authentification as default (no choice) only for the frontend ? \\ The CAS auth for backend is not need, i prefer keep the choice (or better only default auth for backend). --- [[user>M4t]] //2015/13/28 00:00//